package fusing

import (
	"fmt"
	sentinel "github.com/alibaba/sentinel-golang/api"
	"github.com/alibaba/sentinel-golang/core/base"
	"github.com/alibaba/sentinel-golang/core/circuitbreaker"
	"github.com/alibaba/sentinel-golang/core/config"
)

type stateChangeTestListener struct{}

func (s *stateChangeTestListener) OnTransformToClosed(prev circuitbreaker.State, rule circuitbreaker.Rule) {
	fmt.Println("熔断器关闭状态")
}

func (s *stateChangeTestListener) OnTransformToOpen(prev circuitbreaker.State, rule circuitbreaker.Rule, snapshot interface{}) {
	fmt.Println("熔断器打开状态")
}

func (s *stateChangeTestListener) OnTransformToHalfOpen(prev circuitbreaker.State, rule circuitbreaker.Rule) {
	fmt.Println("熔断器半开状态")
}

type SlowRtRatioConfig struct {
	Name                         string
	RetryTimeoutMs               uint32
	MinRequestAmount             uint64
	StatIntervalMs               uint32
	StatSlidingWindowBucketCount uint32
	MaxAllowedRtMs               uint64
	Threshold                    float64
}

type ErrorRatioConfig struct {
	Name                         string
	RetryTimeoutMs               uint32
	MinRequestAmount             uint64
	StatIntervalMs               uint32
	StatSlidingWindowBucketCount uint32
	Threshold                    float64
}

type ErrorCountConfig struct {
	Name                         string
	RetryTimeoutMs               uint32
	MinRequestAmount             uint64
	StatIntervalMs               uint32
	StatSlidingWindowBucketCount uint32
	Threshold                    float64
}

// NewSlowRtRatioConfig 创建一个新的 SlowRtRatioConfig 实例并返回指针
// SlowRtRatioConfig 用于配置慢调用比例熔断策略
func NewSlowRtRatioConfig() *SlowRtRatioConfig {
	return &SlowRtRatioConfig{}
}

// NewErrorRatioConfig 创建一个新的 ErrorRatioConfig 实例并返回指针
// ErrorRatioConfig 用于配置错误比例熔断策略
func NewErrorRatioConfig() *ErrorRatioConfig {
	return &ErrorRatioConfig{}
}

// NewErrorCountConfig 创建一个新的 ErrorCountConfig 实例并返回指针
// ErrorCountConfig 用于配置错误数量熔断策略
func NewErrorCountConfig() *ErrorCountConfig {
	return &ErrorCountConfig{}
}

// initSentinel 初始化 Sentinel 配置
// 该函数会设置 Sentinel 的默认配置，并注册状态变化监听器
// 如果初始化失败，会触发 panic
func initSentinel() {
	// 创建 Sentinel 的默认配置
	conf := config.NewDefaultConfig()
	// 使用配置初始化 Sentinel
	err := sentinel.InitWithConfig(conf)
	if err != nil {
		panic(err) // 如果初始化失败，直接 panic
	}
	// 注册状态变化监听器，用于监听熔断器状态的变化
	circuitbreaker.RegisterStateChangeListeners(&stateChangeTestListener{})
}

// loadRules 加载熔断规则
// 该函数接收一个熔断规则列表，并将其加载到 Sentinel 中
// 如果加载失败，会触发 panic
func loadRules(rules []*circuitbreaker.Rule) {
	// 调用 Sentinel 的 LoadRules 方法加载规则
	_, err := circuitbreaker.LoadRules(rules)
	if err != nil {
		panic(err) // 如果加载失败，直接 panic
	}
}

func (f *SlowRtRatioConfig) SlowRtRatio(data SlowRtRatioConfig) (e *base.SentinelEntry, b *base.BlockError) {
	initSentinel()
	loadRules([]*circuitbreaker.Rule{
		{
			Resource:                     data.Name,
			Strategy:                     circuitbreaker.SlowRequestRatio,
			RetryTimeoutMs:               data.RetryTimeoutMs,
			MinRequestAmount:             data.MinRequestAmount,
			StatIntervalMs:               data.StatIntervalMs,
			StatSlidingWindowBucketCount: data.StatSlidingWindowBucketCount,
			MaxAllowedRtMs:               data.MaxAllowedRtMs,
			Threshold:                    data.Threshold,
		},
	})
	return sentinel.Entry(data.Name, sentinel.WithTrafficType(base.Inbound))
}

func (f *ErrorRatioConfig) ErrorRatio(data ErrorRatioConfig) (e *base.SentinelEntry, b *base.BlockError) {
	initSentinel()
	loadRules([]*circuitbreaker.Rule{
		{
			Resource:                     data.Name,
			Strategy:                     circuitbreaker.ErrorRatio,
			RetryTimeoutMs:               data.RetryTimeoutMs,
			MinRequestAmount:             data.MinRequestAmount,
			StatIntervalMs:               data.StatIntervalMs,
			StatSlidingWindowBucketCount: data.StatSlidingWindowBucketCount,
			Threshold:                    data.Threshold,
		},
	})
	return sentinel.Entry(data.Name, sentinel.WithTrafficType(base.Inbound))
}

func (f *ErrorCountConfig) ErrorCount(data ErrorCountConfig) (e *base.SentinelEntry, b *base.BlockError) {
	initSentinel()
	loadRules([]*circuitbreaker.Rule{
		{
			Resource:                     data.Name,
			Strategy:                     circuitbreaker.ErrorCount,
			RetryTimeoutMs:               data.RetryTimeoutMs,
			MinRequestAmount:             data.MinRequestAmount,
			StatIntervalMs:               data.StatIntervalMs,
			StatSlidingWindowBucketCount: data.StatSlidingWindowBucketCount,
			Threshold:                    data.Threshold,
		},
	})
	return sentinel.Entry(data.Name, sentinel.WithTrafficType(base.Inbound))
}
